<?php
/**
 *
 * Author:苏光盼
 * Date:2020/5/21 下午2:53
 * Class:Common.php
 * Web:https://zoodt.com
 * Project:在线工具
 *
 *
 *
 */

namespace App\Util;


use EasySwoole\Http\Exception\Exception;
use EasySwoole\Http\Request;
use EasySwoole\Http\Response;
use EasySwoole\ORM\AbstractModel;
use EasySwoole\RedisPool\Redis;

final class Common
{

    public static function redis(){
        return Redis::defer('redis');
    }

    public static function del_redis_keys( string $keys = ''):void{
        if(!empty($keys)) {
            $cursor = null;
            $pattern = $keys . '*';
            do {
                $data = self::redis()->scan($cursor, $pattern, 20);
                if ($data) {
                    self::redis()->del(...$data);
                }
            } while ($cursor > 0);
        }
    }

    public static function isPhone( Request $req){
        $agent = strtolower($req->getHeaderLine('user-agent'));
        $clientkeywords = [
            'nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-'
            ,'philips', 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu',
            'android', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini',
            'operamobi', 'opera mobi', 'openwave', 'nexusone', 'cldc', 'midp', 'wap', 'mobile'
        ];
        $is_phone = 'pc';
        if(preg_match("/(".implode('|',$clientkeywords).")/i",$agent))
        {
            $is_phone = 'mobile';
        }
        return $is_phone;
    }

    /**
     * @param $num
     * @param $digits
     * @return string
     * 向下取整 ，保留两位小数
     * 主是要为了提取时，如果有多于分的小数就去掉，以保证不至于亏损
     */
    public static function floorFloat($num,int $digits = 2) {
        $num = floatval($num);
        $multiple = 100;
        $tempNum = floor($num*$multiple);
        return sprintf('%.'.$digits.'f', $tempNum/$multiple);
    }

    /**
     * 任意精度的两个小数相加
     * 2个任意精度数字的加法计算
     */
    public static function myBcadd( $a,$b,$scale=2){
        if(function_exists('bcadd')){
           return  bcadd($a,$b,$scale);
        }
        return self::floorFloat( $a+$b,$scale );
    }

    /**
     * @param $a
     * @param $b
     * @param int $scale
     * @return string|null
     * 2个任意精度的数字除法计算
     */
    public static function myBcdiv($a,$b,$scale=2){
        if(function_exists('bcdiv')){
            return bcdiv($a,$b,$scale);
        }
        return self::floorFloat($a / $b,$scale  );
    }

    /**
     * @param $a
     * @param $b
     * @param int $scale
     * @return string
     * 2个任意精度数字的减法
     */
    public static function myBcsub($a,$b,$scale=2){
        if(function_exists('bcsub')){
            return bcsub($a,$b,$scale);
        }
        return self::floorFloat($a-$b,$scale);
    }

    /**
     * @param $a
     * @param $b
     * @param int $scale
     * @return string
     * 2个任意精度的数字相乘
     */
    public static function myBcmul($a,$b,$scale=2) : string {
        if(function_exists('bcmul')){
            return bcmul($a,$b,$scale);
        }
        return self::floorFloat($a*$b,$scale);
    }
    /**
     * @param $data
     * 写访客日志
     *
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public static function visitor ( $data ):void{
        $spiders = SpiderLog::getSpiderNameByUserAgent($data['agent']);
        if(!$spiders){
            $key = $data['agent_md5'];
            $ret = VisitorLog::create()->where('create_time',Time::__start_today(),'>')
                ->where('agent_md5',$key)->count();
            if(!$ret){
                if(isset($data['referer']) and $data['referer']){
                    $ref_host = parse_url($data['referer']);
                    $data['ref_host'] = $ref_host['host'];
                }
                VisitorLog::create($data)->save();
            } else {
                $ins_data = [
                    'agent_md5' => $key,
                    'url'       => $data['curr_url'],
                ];
                VisitorLogDetail::create($ins_data)->save();
            }
        } else {
            $m = SpiderLog::create()->isHasToDay($spiders,true);
            $m->v += 1;
            $m->update();
        }

    }





    public static function isPhoneNo( $phone ){
        return preg_match("/^1[345789]\d{9}$/", $phone);
    }




    /**
     * @param Request $req
     *
     * @param int $page_size
     *
     * @return array
     * 解析sql的limit语句
     */
    public static function limit( Request $req,int $page_size = 10 )
    {
        $limit = $req->getQueryParams();
        $limit = ( $limit && is_array($limit) ) ? $limit : [];
        $li = ( array_key_exists( 'limit',$limit ) ?
            ( (int)$limit['limit'] > 0 ? (int)$limit['limit'] :  $page_size ) : $page_size);
        $pa = array_key_exists( 'page',$limit)  ? (int)$limit['page'] : 0;
        $page = $li * ($pa - 1 > 0 ? $pa - 1 : 0);
        return [$page,$li];
    }


    public static function getRequestUri( Request $req ) : string{
        return trim(
            str_replace(
                URL_HTML_SUFFIX,
                '',
                $req->getSwooleRequest()->server['request_uri']
            )
            ,'/'
        );
    }



    /**
     * @param array $data
     * @param string|null $child
     *
     * @return array
     * 创建树形菜单 主要是针对后台用户组的权限菜单
     */
    static public function create_menu(array $data, ?string $child = 'child'){
        $items = [];
        foreach ($data as $v) {
            $t_v = $v;
            if($v instanceof AbstractModel){
                $t_v = $v->toArray();
            }
            $items[$v['id']] = $t_v;
        }
        $tree = [];
        foreach ($items as $k => $item) {
            if ($item['pid'] > 0) {
                $items[$item['pid']][$child][] = &$items[$k];
            } else {
                $tree[] = &$items[$k];
            }
        }
        return $tree;
    }


    public static function clean_html($str,$a = null)
    {
        $str = strip_tags($str,$a);
        $str = trim($str);
        $str = preg_replace("/\s+/"," ", $str);
        //      $str = preg_replace("/ /","",$str);
//        $str = preg_replace("/&nbsp;/","",$str);
        $str = preg_replace("/　/"," ",$str);
        $str = preg_replace("/\r\n/","",$str);
        $str = str_replace(chr(13),"",$str);
        $str = str_replace(chr(10),"",$str);
        $str = str_replace(chr(9),"",$str);
        return $str;
    }


    /**
     * @param string $str
     *
     * @return string
     * 加密字符串
     */
    public static function encode(string $str)
    {
        $key_content = file_get_contents(EASYSWOOLE_ROOT.'/rsa-key/down_pub.key');
        $pubKeyId    = openssl_get_publickey($key_content);
        if (openssl_public_encrypt($str, $cryptText, $pubKeyId))
        {
            return base64_encode("".$cryptText);
        }
    }

    /**
     * @param string $cryptText
     *
     * @return string
     * 解密 前端用jsencrypt.js加密过的数据，是通过rsa公匙加密过的
     * @throws DecodeException
     */
    public static function decode( string  $cryptText)
    {
        $key = file_get_contents(EASYSWOOLE_ROOT . '/rsa-key/down.key');
        $priKeyId = openssl_get_privatekey($key);
        $cryptText = base64_decode($cryptText);
        if( openssl_private_decrypt($cryptText,$str,$priKeyId,OPENSSL_PKCS1_PADDING)){
            return "".$str;
        }
        throw new DecodeException('参数错误！',403);
    }


    /**
     * @param Response $rep
     * 为每个用户设置不一样的Cookie
     * @param Request $req
     * @param $expire
     *
     */
    public static function setCookie( Response $rep, Request $req,int $expire = 0){
        $cookie_value = self::cookie_value($req);
        $t_t = $expire > 0 ? $expire + time() : null;
        $rep->setCookie(COOKIE_UNIQUE,$cookie_value,
            $t_t,'/',parse_url(WEB_HOST)['host']);
    }

    public static function cookie_value( Request $req)
    {
        $ip = $req->getHeaderLine('x-real-ip');
        $ua = $req->getHeaderLine('user-agent');
        $al = $req->getHeaderLine('accept-language');
        $host = $req->getHeaderLine('host');
        return md5(md5($ip . $ua . $al . $host ) . time());
    }

    public static function user_agent_id( Request $req, $agent = null ): string {
        $ua = !empty($agent) ? $agent :$req->getHeaderLine('user-agent');
        $ip = $req->getHeaderLine('x-real-ip');
        return md5(md5($ip) . md5(strtolower($ua)));
    }

    /**
     * @param array $arr
     * @param $field
     *
     * @return array
     * 只保留数组中的 $field
     */
    public static function filter_array( array $arr, $field) : array {
        is_array($field) OR $field = [$field];
        $ret = [];
        foreach ($field as $item){
            $ret[$item] = array_key_exists($item, $arr) ? $arr[$item] : null;
        }
        return $ret;
    }

    public static function check_array( array $arr ,$field){
        is_array($field) OR $field = [$field];
        $ret = [];
        foreach ($field as $item){
            if( ! array_key_exists($item, $arr)) throw new Exception("the {$item} not exists!",500);
        }
        return $arr;
    }


    /**
     * @param int $v
     *
     * @return string
     * 格式化字节
     */
    public static function format_kb( int $v){
        $units = array(
            'B',
            'KB',
            'MB',
            'GB',
            'TB',
            'PB',
            'EB',
            'ZB',
            'YB'
        );
        for ($i = 0; $v >= 1024 && $i < 8; $i ++)
            $v /= 1024;
        return round($v, 2) . ' ' . $units[$i];
    }

    public static function check(Request $req,string $field = 'id', $method = 'get' ) {
        //$url_path = $req->getUri()->getPath();
        $err_msg = "the {$field} is not exists!";
        if( !$field ) throw new  Exception($err_msg,404);;
        if($method == 'get'){
            $arg = $req->getQueryParams();
        } else {
            $arg = $req->getParsedBody();
        }
        if ( isset($arg[$field]) && $arg[$field] ) {
            return is_string($arg[$field]) ? trim($arg[$field]) : (is_array($arg[$field]) ? array_filter($arg[$field]) : $arg[$field] ) ;
        }
        throw new IndexException($err_msg,404);
    }

    /**
     * 验证订单ID的格式是否正确
     * @param string $trade_id
     *
     * @return string|null
     * @throws IndexException
     */
    public static function check_trade_id(string  $trade_id):?string{
        $trade_id = self::clean_html($trade_id);
        if(is_numeric($trade_id)) throw new IndexException('参数错误！',403);
        if( strlen($trade_id) != 36) throw new IndexException('参数错误！',403);
        $tem_arr = explode('-',$trade_id);
        if(count($tem_arr) !== 5){
            throw new IndexException('参数错误！',403);
        }
        return $trade_id;
    }

    public static function is_ajax( Request $req) : bool {
        return array_key_exists('x-requested-with',$req->getSwooleRequest()->header)
            and $req->getSwooleRequest()->header['x-requested-with'] == 'XMLHttpRequest';
    }

    /**
     * @param string $str
     *
     * @return array
     * 格式化一下类似这样的字符串：a|1,b|2,c|3, ==> [a=>1,b=>2,c=>3]
     */
    public static function format_config( string  $str){
        $t2 = [];
        if($config = trim($str,',')){
            $t1 = explode(',',$config);
            foreach($t1 as $v){
                $t3 = explode('|',$v);
                if($t3 and count($t3) === 2){
                    $t2 += [$t3[0] => $t3[1]];
                }
            }
        }
        return $t2;
    }
    /**
     * @param $string
     *
     * @return int
     * 检查是否包含中文
     */
    public static function checkChinese($string) {
        if(preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', $string) === 1){
            //全是中文
            return 1;
        }elseif(preg_match('/[\x{4e00}-\x{9fa5}]/u', $string) === 1){
            //包含中文
            return 0;
        }
        return -1;
    }

    /**
     * 截取中英混排字符串
     *
     * @param (string) $string
     * @param int $sublen
     * @param int $start
     * @param string $code
     * @param string $omitted
     *
     * @return string
     */

    public static function sb_substr($string, $sublen = 100, $start = 0, $code = 'UTF-8', $omitted = '...'){
        $string = self::clean_html($string);
        if($code == 'UTF-8'){
            $pa ="/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/";
            preg_match_all($pa, $string, $t_string);
            if(count($t_string[0]) - $start > $sublen) return join('', array_slice($t_string[0], $start, $sublen)).$omitted;
            return join('', array_slice($t_string[0], $start, $sublen));
        } else  {
            $start = $start*2;
            $sublen = $sublen*2;
            $strlen = strlen($string);
            $tmpstr = '';
            for($i=0; $i<$strlen; $i++) {
                if($i>=$start && $i<($start+$sublen)) {
                    if(ord(substr($string, $i, 1))>129) {
                        $tmpstr.= substr($string, $i, 2);
                    }  else  {
                        $tmpstr.= substr($string, $i, 1);
                    }
                }
                if(ord(substr($string, $i, 1))>129) $i++;
            }
            if(strlen($tmpstr)<$strlen ) $tmpstr.= $omitted;
            return $tmpstr;
        }
    }


    public static function adv_list(Request $req, $mode , $with = null){
        $where = $req->getQueryParams();
        $limit = $where['limit'];$page = $where['page'];
        unset($where['limit'],$where['page']);
        $mode = $mode::create()->page($page,$limit)->order('id');
        if($with){
            $mode->with($with);
        }
       $t_rows = false;
        if( method_exists( $mode,'getRows' ) ){
            $rows = $mode::getRows();
        } else {
            $mode->withTotalCount();
            $t_rows = true;
        }
        $where = array_filter($where);
        if($where){
            foreach($where as $k => $v){
                if( $v ) {
                    $mode->where($k,$v);
                }
            }
        }
        $data = $mode->all();
        if( $t_rows ){
            $rows = $mode->lastQueryResult()->getTotalCount();
        }
        return [$data,$rows];
    }

    public static function make_key_tosbc(string $str, string $type = 'tosbc') {
        $dbc = array(
            '０' , '１' , '２' , '３' , '４' ,
            '５' , '６' , '７' , '８' , '９' ,
            'Ａ' , 'Ｂ' , 'Ｃ' , 'Ｄ' , 'Ｅ' ,
            'Ｆ' , 'Ｇ' , 'Ｈ' , 'Ｉ' , 'Ｊ' ,
            'Ｋ' , 'Ｌ' , 'Ｍ' , 'Ｎ' , 'Ｏ' ,
            'Ｐ' , 'Ｑ' , 'Ｒ' , 'Ｓ' , 'Ｔ' ,
            'Ｕ' , 'Ｖ' , 'Ｗ' , 'Ｘ' , 'Ｙ' ,
            'Ｚ' , 'ａ' , 'ｂ' , 'ｃ' , 'ｄ' ,
            'ｅ' , 'ｆ' , 'ｇ' , 'ｈ' , 'ｉ' ,
            'ｊ' , 'ｋ' , 'ｌ' , 'ｍ' , 'ｎ' ,
            'ｏ' , 'ｐ' , 'ｑ' , 'ｒ' , 'ｓ' ,
            'ｔ' , 'ｕ' , 'ｖ' , 'ｗ' , 'ｘ' ,
            'ｙ' , 'ｚ' , '－' , '　' , '：' ,
            '．' , '，' , '／' , '％' , '＃' ,
            '！' , '＠' , '＆' , '（' , '）' ,
            '＜' , '＞' , '＂' , '＇' , '？' ,
            '［' , '］' , '｛' , '｝' , '＼' ,
            '｜' , '＋' , '＝' , '＿' , '＾' ,
            '￥' , '￣' , '｀');
        $sbc = array(
            '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E',
            'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O',
            'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y',
            'Z', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i',
            'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's',
            't', 'u', 'v', 'w', 'x',
            'y', 'z', '-', ' ', ':',
            '.', ',', '/', '%', ' #',
            '!', '@', '&', '(', ')',
            '<', '>', '"', '\'','?',
            '[', ']', '{', '}', '\\',
            '|', '+', '=', '_', '^',
            '￥','~', '`');
        if($type == 'todbc'){
            return str_replace( $sbc, $dbc, $str ); //半角到全角
        }elseif($type == 'tosbc'){
            return str_replace( $dbc, $sbc, $str ); //全角到半角
        }else{
            return $str;
        }
    }

    /**
     * @param array $data
     * @param string $field
     * 为某个二维数组增加ip位置
     */
    public static function append_ip_region(array &$data,$field = 'ip') {
        if($data and $field){
            foreach($data as $k=>$v){
                if(!isset( $v->{$field} ) OR !$v->{$field}) break;
                $ip_city_data = Ip2Region::getInstance()->memorySearch($v->{$field});
                $data[$k]->ip_region =  $ip_city_data['region'];
            }
        }
    }


    /**
     * @param $time
     * @return int|string
     * 格式化秒的输出
     */
    public static function format_second($time) {
        $output = '';
        foreach (array(86400 => '天', 3600 => '小时', 60 => '分', 1 => '秒') as $key => $value) {
            if ($time >= $key) $output .= floor($time/$key) .' '. $value;
            $time %= $key;
        }
        return $output ? $output : '0 秒';
    }


    /**
     * @return string
     * 生成随机验证码
     */
    public static function rand_code():string{
        $code = md5(uniqid());
        self::redis()->setEx(REDIS_COMMENTS_RAND_CODE . $code,60*60*1,$code);
        return $code;
    }

    /**
     * @param string $code
     * @return bool
     * 验证随机码，正确的话就删除
     */

    public static function rand_code_checkout( string $code):bool{
        if(empty($code)) return false;
        $is = self::redis()->exists( REDIS_COMMENTS_RAND_CODE . $code);
        if($is){
            self::redis()->del(REDIS_COMMENTS_RAND_CODE . $code);
            return true;
        }
        return false;
    }


    /**
     * @param $filepath
     * @return bool
     */
    public static function file_is_exists($filepath){
        return is_file($filepath);
    }
}